package com.studyroom.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.profile.DefaultProfile;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.gson.Gson;
import com.studyroom.dto.LoginFormDTO;
import com.studyroom.dto.PwdForm;
import com.studyroom.dto.Result;
import com.studyroom.dto.UserDTO;
import com.studyroom.entity.Icon;
import com.studyroom.entity.User;
import com.studyroom.mapper.UserMapper;
import com.studyroom.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.studyroom.utils.CacheClient;
import com.studyroom.utils.Md5Utils;
import com.studyroom.utils.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.studyroom.utils.DefaultContants.*;
import static com.studyroom.utils.RedisContants.*;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 
 * @since 2022-11-21
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private CacheClient cacheClient;

    //Shiro登录方法
    @Override
    public User login(String principal) {
        log.info ("principal-->"+principal);
        if(principal.length ()==11&&NumberUtil.isNumber (principal)){
            User user=query ().eq ("phone",principal).one ();
            return user;
        }else if(principal.length ()<11){
            User user=query ().eq ("username",principal).one ();
            return user;
        }
        return null;
    }

    @Override
    public Result loginSuccess() {
        UserDTO me=UserHolder.getUser ();
        //用户存在,UUID生成token
        String token= UUID.randomUUID ().toString (true);
        String tokenKey=TOKEN_KEY+token;
        String userKey=USER_KEY+me.getId ();
        //用token作为key,将用户数据UserDTO写入redis
        cacheClient.set (tokenKey,me,TOKEN_TTL, TimeUnit.MINUTES);
        cacheClient.set (userKey,me,USER_TTL,TimeUnit.DAYS);
        //返回token
        return Result.ok (token);
    }

    @Override
    public Result login(LoginFormDTO loginFormDTO) {
        //从数据库查询用户
        QueryWrapper wrapper=new QueryWrapper ();
        if(StrUtil.isNotBlank (loginFormDTO.getUsername ())) {
            wrapper.eq ("username", loginFormDTO.getUsername ());
        }else {
            wrapper.eq ("phone", loginFormDTO.getPhone ());
        }
        wrapper.eq ("password",loginFormDTO.getPassword ());
        User user=getOne (wrapper);
        if(user==null){
            //用户不存在,返回错误信息
            return Result.fail ("用户名,手机号或密码错误");
        }
        //用户存在,UUID生成token
        String token= UUID.randomUUID ().toString (true);
        String tokenKey=TOKEN_KEY+token;
        String userKey=USER_KEY+user.getId ();
        //用token作为key,将用户数据UserDTO写入redis
        UserDTO userDTO= BeanUtil.copyProperties (user,UserDTO.class);
        cacheClient.set (tokenKey,userDTO,TOKEN_TTL, TimeUnit.MINUTES);
        cacheClient.set (userKey,userDTO,USER_TTL,TimeUnit.DAYS);
        //返回token
        return Result.ok (token);
    }


    @Override
    public Result register(UserDTO user) {
        //1.校验信息是否规范
        Result result=registerVerify (user);
        if(!result.isSuccess ()){
            return result;
        }
        //2.校验验证码是否存在
        String verifyCodeKey=VERIFY_CODE_KEY+user.getPhone ();
        String verifyCode=stringRedisTemplate.opsForValue ().get (verifyCodeKey);
        if(StrUtil.isBlank (verifyCode)){
            //验证码不存在
            return Result.fail ("请先获取验证码");
        }
        //验证码存在，验证是否正确
        if(!StrUtil.equals (user.getVerifyCode (),verifyCode)){
            //不正确
            return Result.fail ("验证码错误");
        }
        //删除redis中的验证码
        stringRedisTemplate.delete (verifyCodeKey);
        //正确，开始保存用户数据到数据库
        user.setIcon (USER_ICON);   //默认头像
        user.setBirthday (LocalDateTime.now ());//创建日期
        user.setStatus (1);//默认用户权限
        User newUser=BeanUtil.copyProperties (user,User.class);//复制信息到User类
        newUser.setPassword (Md5Utils.encrypt (newUser.getPassword ()));
        boolean flag=save (newUser);//保存
        if(!flag){
            return Result.fail ("注册失败");
        }
        //保存成功，将用户写入redis
        User queryUser=query ().eq ("username",user.getUsername ()).one ();
        if(queryUser==null){
            return Result.fail ("注册失败");
        }
        String userKey=USER_KEY+queryUser.getId ();
        stringRedisTemplate.opsForValue ().set (userKey,JSONUtil.toJsonStr (queryUser));
        stringRedisTemplate.expire (userKey,USER_TTL,TimeUnit.DAYS);
        return Result.ok ();
    }

    @Override
    public Result logout(HttpServletRequest req) {
        String token=req.getHeader ("authorization");
        String tokenKey=TOKEN_KEY+token;
        String userKey=USER_KEY+token;
        //删除redis中的token
        stringRedisTemplate.delete (tokenKey);
        return Result.ok ();
    }

    @Override
    public Result me() {
        //获取前端发来得token
        UserDTO me=UserHolder.getUser ();
        User user=this.getById (me.getId ());
        UserDTO userDTO=BeanUtil.copyProperties (user,UserDTO.class);
        return Result.ok (userDTO);
    }

    @Override
    public User getById(Long id) {
        //从redis中查询
        String userKey=USER_KEY+id;
        String userJson=stringRedisTemplate.opsForValue ().get (userKey);
        if(StrUtil.isNotBlank (userJson)) {
            User user = JSONUtil.toBean (userJson, User.class);
            return user;
        }
        //redis不存在，从数据库查询
        User user=getBaseMapper ().selectById (id);
        //写入redis
        stringRedisTemplate.opsForValue ().set (userKey,JSONUtil.toJsonStr (user));
        return user;
    }
    /*修改密码*/
    @Override
    public Result updatePwd(PwdForm pwdForm) {
        Result result=PwdVerify (pwdForm);
        if(!result.isSuccess ()){
            return result;
        }
        UserDTO me= UserHolder.getUser ();
        //从数据库查询旧密码是否正确
        User user=getBaseMapper ().selectById (me.getId ());
        String oldPwdMd5=Md5Utils.encrypt (pwdForm.getOldPwd ());
        if(!oldPwdMd5.equals (user.getPassword ())){
            return Result.fail ("旧密码输入错误");
        }
        //验证新密码是否与旧密码一致
        //加密新密码
        String newPwdMd5=Md5Utils.encrypt (pwdForm.getNewPwd ());
        if(newPwdMd5.equals (user.getPassword ())){{
            return Result.fail ("新密码与旧密码一致");
        }}
        //旧密码正确，修改新密码

        user.setPassword (newPwdMd5);
        boolean b=updateById (user);
        if(!b){
            return Result.fail ("修改失败");
        }
        return Result.ok();
    }

    @Override
    public Result getIcons() {
        List<Icon> icons=getBaseMapper ().getIcons ();
        return Result.ok (icons);
    }
    /*修改头像*/
    @Override
    public Result updateIcon(Icon icon) {
        UserDTO me=UserHolder.getUser ();
        boolean b=update ().eq ("id",me.getId ()).set ("icon",icon.getUrl ()).update ();
        if(!b){
            return Result.fail ("修改失败");
        }
        //修改成功,删除redis中的用户信息
        String userKey=USER_KEY+me.getId ();
        stringRedisTemplate.delete (userKey);
        return Result.ok ();
    }

    @Override
    public Result getRegisterVerifyCode(String phone) {
        //1.手机格式校验
        if(phone.length ()!=11||!NumberUtil.isNumber (phone)){
            return Result.fail ("手机号码格式错误");
        }
        //2.从数据库查询该手机号码
        User user=query ().eq ("phone",phone).one ();
        if(user!=null){
            return Result.fail ("该手机号码已经被注册");
        }
        Result result=sendVerifyCode (phone);
        if(!result.isSuccess ()){
            return result;
        }
        //成功!
        return Result.ok ();
    }
    public Result sendVerifyCode(String phone){
        //1.查询该验证码5分钟内是否被获取过
        String verifyKey=VERIFY_CODE_KEY+phone;
        Long count=stringRedisTemplate.getExpire (verifyKey);
        if(count>0){
            return Result.fail ("每5分钟只能获取一次验证码");
        }
        //验证通过  开始发送验证码到用户手机
        //随机生成6位数字验证码
        Integer codeInt= RandomUtil.randomInt (100000,999999);
        String code=StrUtil.toString (codeInt);
        //短信变量
        Map<String, Object> map = new HashMap<> ();
        map.put("code", code);

/*        三个参数：
        regionId    cn-hangzhou
        accessKeyId 自己的用户accessKeyId
        accessSecret 自己的用户accessSecret*/
        DefaultProfile profile=DefaultProfile.getProfile ("cn-hangzhou",ACCESS_KEY,ACCESS_KEY_SECRET);

        //构建请求
        IAcsClient client = new DefaultAcsClient (profile);
        SendSmsRequest request = new SendSmsRequest();
        request.setPhoneNumbers(phone);//接收短信的手机号码
        request.setSignName("胡梓烁的博客");//短信签名名称
        request.setTemplateCode("SMS_264790013");//短信模板CODE
        request.setTemplateParam(JSONObject.toJSONString(map));//短信模板变量对应的实际值

        try {
            SendSmsResponse response = client.getAcsResponse(request);
            // 发送短信
            new Gson ().toJson(response);
            if(!response.getCode ().equals ("OK")){
                log.info ("验证码获取结果"+response.getCode ()+",消息:"+response.getMessage ());
                return Result.fail ("获取验证码失败");
            }
            // 将验证码放置在redis缓存中，并设置5分钟有效时间
            String verifyCodeKey=VERIFY_CODE_KEY+phone;
            stringRedisTemplate.opsForValue().set(verifyCodeKey, code, 5, TimeUnit.MINUTES);
        } catch (ServerException e) {
            e.printStackTrace();
            return Result.fail ("获取短信失败");
        } catch (ClientException e) {
            e.printStackTrace();
            return Result.fail ("获取短信失败");
        }
        return Result.ok ();

    }


    @Override
    public Result loginByVerifyCode(UserDTO userDTO) {
        //1.校验手机号码格式
        if(userDTO.getPhone ().length ()!=11||!NumberUtil.isNumber (userDTO.getPhone ())){
            return Result.fail ("手机格式错误");
        }
        //2.校验验证码格式
        if(userDTO.getVerifyCode ().length ()!=6||!NumberUtil.isNumber (userDTO.getVerifyCode ())){
            return Result.fail ("验证码格式错误");
        }
        //从redis中获取验证码
        String verifyCodeKey=VERIFY_CODE_KEY+userDTO.getPhone ();
        String verifyCode=stringRedisTemplate.opsForValue ().get (verifyCodeKey);
        if(StrUtil.isBlank (verifyCode)){
            return Result.fail ("请先获取验证码");
        }
        //存在，校验是否正确
        if(!StrUtil.equals (userDTO.getVerifyCode (),verifyCode)){
            //不正确
            return Result.fail ("验证码错误");
        }
        //删除redis中的验证码
        stringRedisTemplate.delete (verifyCodeKey);
        //校验成功，从数据库看查询用户
        User user=query ().eq ("phone",userDTO.getPhone ()).one ();
        if(user==null){
            return Result.fail ("登录失败");
        }
        //获取用户数据成功，保存token到redis
        // 生成UUID作为tokenKey
        String token=UUID.randomUUID ().toString (true);
        String tokenKey=TOKEN_KEY+token;
        UserDTO userDTO2=BeanUtil.copyProperties (user,UserDTO.class);
        stringRedisTemplate.opsForValue ().set (tokenKey,JSONUtil.toJsonStr (userDTO2),TOKEN_TTL,TimeUnit.MINUTES);
        return Result.ok (token);
    }

    @Override
    public Result getLoginVerifyCode(String phone) {
        //1.手机格式校验
        if(phone.length ()!=11||!NumberUtil.isNumber (phone)){
            return Result.fail ("手机号码格式错误");
        }
        //2.从数据库查询该手机号码
        User user=query ().eq ("phone",phone).one ();
        if(user==null){
            return Result.fail ("该手机号码未注册");
        }
        Result result=sendVerifyCode (phone);
        if(!result.isSuccess ()){
            return result;
        }
        //成功!
        return Result.ok ();
    }

    @Override
    public Result getOneById(Long id) {
        User user=this.getById (id);
        if(user==null){
            return Result.fail ("获取用户信息失败");
        }
        return Result.ok (user);
    }


    private Result PwdVerify(PwdForm pwdForm){
        if(pwdForm.getOldPwd ().length ()<6||pwdForm.getOldPwd ().length ()>16||
        pwdForm.getNewPwd ().length ()<6||pwdForm.getNewPwd ().length ()>16||
                pwdForm.getConfirmPwd ().length ()<6||pwdForm.getConfirmPwd ().length ()>16
        ){
            return Result.fail ("密码格式错误");
        }
        return Result.ok ();
    }
    public Result registerVerify(UserDTO user){
        System.out.println ("user ----> "+user.toString ());
        if(user.getUsername ().length ()<3||user.getUsername ().length ()>10){
            return Result.fail ("用户名长度在 3~10 位之间");
        }
        User queryUser=null;
        queryUser=query ().eq ("username",user.getUsername ()).one ();
        System.out.println ("queryUser -->"+queryUser);
        if(queryUser!=null){
            return Result.fail ("该用户名已被注册");
        }
        if(user.getPassword ().length ()<6||user.getPassword ().length ()>16){
            return Result.fail ("密码长度在 6~16 位之间");
        }
        if(user.getPhone ().length ()!=11|| !NumberUtil.isNumber (user.getPhone ())){
            return Result.fail ("密码格式错误");
        }
        queryUser=query ().eq ("phone",user.getPhone ()).one();
        if(queryUser!=null){
            return Result.fail ("该手机号已被注册");
        }
        return Result.ok ();
    }

}
